package org.chaiyi.template.core.register.contractor.router;

import lombok.RequiredArgsConstructor;
import org.chaiyi.template.core.common.common.BeanNameManage;
import org.chaiyi.template.core.common.contract.ContractInfo;
import org.chaiyi.template.core.common.contract.ContractUtil;
import org.chaiyi.template.core.common.contract.ContractorMethodName;
import org.chaiyi.template.core.common.contract.method.*;
import org.chaiyi.template.core.common.repository.QueryInfo;
import org.chaiyi.template.core.common.util.asserts.Asserts;
import org.chaiyi.template.core.framework.swagger.RouterFunctionOperationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 自动生成接口配置类
 *
 * @author Gong.Yang
 */
@Configuration
@RequiredArgsConstructor
public class ContratorRouterConfig {

    private final ApplicationContext context;

    @Bean
    public RouterFunction<ServerResponse> createOneRouter(List<CreateOneContractor> list) {
        if (Asserts.isEmpty(list)) return null;
        RouterFunctions.Builder builder = RouterFunctions.route();
        for (CreateOneContractor createOneContractor : list) {
            Class<?> iService = ContractUtil.getIService(createOneContractor.getClass().getSuperclass());
            String beanName = BeanNameManage.getContractInfoName(iService);
            ContractInfo info = context.getBean(beanName, ContractInfo.class);
            builder.POST(info.getPath(),
                    request -> {
                        String tenantId = request.pathVariable("tenantId");
                        String buCode = request.pathVariable("buCode");

                        return ServerResponse.ok().body(
                                request.bodyToMono(info.getEntity())
                                        .map(entity -> createOneContractor.createBefore(tenantId, buCode, entity))
                                        .flatMap(entity -> createOneContractor.create(tenantId, buCode, entity))
                                        .doOnSuccess(entity -> createOneContractor.createAfter(tenantId, buCode, entity))
                                , info.getEntity());
                    });
            RouterFunctionOperationContext.push(info, ContractorMethodName.CREATE);
        }
        return builder.build();
    }

    @Bean
    public RouterFunction<ServerResponse> queryRouter(List<QueryContractor> list) {
        if (Asserts.isEmpty(list)) return null;
        RouterFunctions.Builder builder = RouterFunctions.route();
        for (QueryContractor queryContractor : list) {
            Class<?> iService = ContractUtil.getIService(queryContractor.getClass().getSuperclass());
            String beanName = BeanNameManage.getContractInfoName(iService);
            ContractInfo info = context.getBean(beanName, ContractInfo.class);
            builder.POST(info.getPath() + "/query",
                    request -> {
                        String tenantId = request.pathVariable("tenantId");
                        String buCode = request.pathVariable("buCode");

                        Flux<Object> flux = request.bodyToMono(QueryInfo.class)
                                .flatMapMany(queryInfo -> queryContractor.query(tenantId, buCode, queryInfo));

                        return ServerResponse.ok().body(flux, info.getEntity());
                    });
            RouterFunctionOperationContext.push(info, ContractorMethodName.QUERY);
        }
        return builder.build();
    }

    @Bean
    public RouterFunction<ServerResponse> getByIdRouter(List<GetByIdContractor> list) {
        if (Asserts.isEmpty(list)) return null;
        RouterFunctions.Builder builder = RouterFunctions.route();
        for (GetByIdContractor contractor : list) {
            Class<?> iService = ContractUtil.getIService(contractor.getClass().getSuperclass());
            String beanName = BeanNameManage.getContractInfoName(iService);
            ContractInfo info = context.getBean(beanName, ContractInfo.class);
            builder.GET(info.getPath() + "/getById/{id}",
                    request -> {
                        String tenantId = request.pathVariable("tenantId");
                        String buCode = request.pathVariable("buCode");
                        String id = request.pathVariable("id");
                        return ServerResponse.ok().body(contractor.getById(tenantId, buCode, id), info.getEntity());
                    });
            RouterFunctionOperationContext.push(info, ContractorMethodName.FIND_BY_ID);
        }
        return builder.build();
    }

    @Bean
    public RouterFunction<ServerResponse> countRouter(List<CountContractor> list) {
        if (Asserts.isEmpty(list)) return null;
        RouterFunctions.Builder builder = RouterFunctions.route();
        for (CountContractor contractor : list) {
            Class<?> iService = ContractUtil.getIService(contractor.getClass().getSuperclass());
            String beanName = BeanNameManage.getContractInfoName(iService);
            ContractInfo info = context.getBean(beanName, ContractInfo.class);
            String path = info.getPath() + "/count";
            builder.POST(path,
                    request -> {
                        String tenantId = request.pathVariable("tenantId");
                        String buCode = request.pathVariable("buCode");

                        Mono<Long> mono = request.bodyToMono(QueryInfo.class)
                                .flatMap(queryInfo -> contractor.count(tenantId, buCode, queryInfo));

                        return ServerResponse.ok().body(mono, Long.class);
                    });
            RouterFunctionOperationContext.push(info, ContractorMethodName.COUNT);
        }
        return builder.build();
    }


    @Bean
    public RouterFunction<ServerResponse> deletedRouter(List<DeleteContractor> list) {
        if (Asserts.isEmpty(list)) return null;
        RouterFunctions.Builder builder = RouterFunctions.route();
        for (DeleteContractor contractor : list) {
            Class<?> iService = ContractUtil.getIService(contractor.getClass().getSuperclass());
            String beanName = BeanNameManage.getContractInfoName(iService);
            ContractInfo info = context.getBean(beanName, ContractInfo.class);
            builder.DELETE(info.getPath() + "/{id}",
                    request -> {
                        String tenantId = request.pathVariable("tenantId");
                        String buCode = request.pathVariable("buCode");
                        String id = request.pathVariable("id");
                        contractor.deleteBefore(tenantId, buCode, id);
                        Mono mono = contractor.delete(tenantId, buCode, id)
                                .doOnSuccess(entity -> contractor.deleteAfter(tenantId, buCode, entity));
                        return ServerResponse.ok().body(mono, info.getEntity());
                    });
            RouterFunctionOperationContext.push(info, ContractorMethodName.DELETE);
        }
        return builder.build();
    }


    @Bean
    public RouterFunction<ServerResponse> modifyRouter(List<ModifyContractor> list) {
        if (Asserts.isEmpty(list)) return null;
        RouterFunctions.Builder builder = RouterFunctions.route();
        for (ModifyContractor contractor : list) {
            Class<?> iService = ContractUtil.getIService(contractor.getClass().getSuperclass());
            String beanName = BeanNameManage.getContractInfoName(iService);
            ContractInfo info = context.getBean(beanName, ContractInfo.class);
            builder.PUT(info.getPath(),
                    request -> {
                        String tenantId = request.pathVariable("tenantId");
                        String buCode = request.pathVariable("buCode");

                        return ServerResponse.ok().body(
                                request.bodyToMono(info.getEntity())
                                        .map(entity -> contractor.modifyBefore(tenantId, buCode, entity))
                                        .flatMap(entity -> contractor.modify(tenantId, buCode, entity))
                                        .doOnSuccess(entity -> contractor.modifyAfter(tenantId, buCode, entity))
                                , info.getEntity());
                    });
            RouterFunctionOperationContext.push(info, ContractorMethodName.MODIFY);
        }
        return builder.build();
    }

}
